/*
 * ws_data.c
 *
 *  Created on: Jun 25, 2020
 *      Author: daniel
 */


#include "ws_data.h"
#include <stdio.h>
#include <string.h>

#include "LoRA.h"
#include "INA219.h"
#include "OTA/ota.h"
#include "md5.h"

#include "SHT3x/sht3x.h"
#include "illumination/MAX44009.h"
#include "pressure/ms5611.h"
#include "solarradiation/ads1115.h"
#include "uv/VEML6075.h"
#include "wind/wind.h"
#include "rain/rain.h"
#include "lightning/AS3935.h"
#include "pack.h"

#define SEND_PERIOD			1		// 0:1秒； 1：2秒；2：3秒，以此类推

// 需要全速运行的标记
uint8_t		full_flag = 0x0;

// 气象站的所有数据dataMask
WS_DATA		s_Data = {
		.dataMask = 0,
		.temperature = -333,			// 单位：0.01度
		.humidity = 0,				// 单位：0.01%
		.wind_dir = 0,				// 单位：度
		.wind_speed = 0,			// 单位：0.1m/s
		.pressure = 0,				// 单位：mbar
		.solar_radiation = 0,		// 单位：w/m2
		.illumination = 0,			// 单位：lux
		.uv_index = 0,				// 单位：0.01
		.uva_value = 0,				// 单位：0.01
		.uvb_value = 0,				// 单位：0.01
		.rain = 0,					// 单位：0.1mm（距离上次上报后的雨量汇总）
		.lightning_dist = 0,		// 0:表示没有雷电，其他值表示雷电发生距离(KM)

		.battery_voltage = 0,		// 单位：0.01v
		.battery_current = 0,		// 单位：0.1ma

		.firmware = FIRMWARE_NONE,
};

// 上次ACK的数据，用来做差异性发送的依据
//WS_DATA		s_LastData;

// 变更数据
//WS_DATA		s_NewData;

uint8_t				sendBufferSize = 0;
static uint8_t		sendBuffer[MAX_LORA_BUFFER_SIZE];
static uint8_t		send_no = 0xFF;

uint8_t				receiveBufferSize = 0;
uint8_t				receiveBuffer[MAX_LORA_BUFFER_SIZE];

// 确认流程，将发送的数据更新到确认数据

// 重发计数器
static uint8_t			data_send_retry = 0;
static uint8_t			get_ota_retry = 0;
#define MAX_RETRY		2
#define MAX_OTA_RETRY	5					// OTA数据获取，如果连续5次重试没能响应，则进入低功耗状态，等待下一秒的指示

// 长时间没有确认，则不再重发，保持1秒一次
static uint8_t				no_ack_count = 0;
#define MAX_NO_ACK_COUNT	10				// 大约120秒没有得到主机确认，则不再进行重发

// 正在发送标记
typedef struct {
	uint8_t		full_run:1;		// 指示，是否全速允许，不休眠
	uint8_t		sending:1;
	uint8_t		ws_data:1;
	uint8_t		ota:1;
} RUNNING_FLAG;
static RUNNING_FLAG			flag = {
		.full_run = 0,
		.sending = 0,
		.ws_data = 0,
		.ota = 0,
};

// 新版本标记

enum TimerStatus {
	TIMER_NONE,
	START_MEASURE,
	GET_DATA,
	START_MEASURE_MS5611_1,		// MS5611t特殊处理，需要提前一个时刻进行D2数据的测量
};

// I2C总线读取异常标记和重试次数
static bool		i2c_ok = true;
static uint8_t	i2c_reinit_count = 0;
#define MAX_I2C_REINIT		3

extern I2C_HandleTypeDef hi2c1;

static enum TimerStatus	timerStatus = TIMER_NONE;

extern LPTIM_HandleTypeDef hlptim1;

extern IWDG_HandleTypeDef hiwdg;

static uint32_t	wsCount = 0;

// local function
void ws_rxtimeout_callback(void* data);

void HAL_LPTIM_CompareMatchCallback(LPTIM_HandleTypeDef *hlptim)
{
	static uint8_t	time_count = 0;

	// 每125ms一次
	time_count++;
	wsCount++;

	switch( time_count )
	{
	case (SEND_PERIOD * 8) + 6:
		timerStatus = START_MEASURE_MS5611_1;
		break;

	case (SEND_PERIOD * 8) + 7:
		timerStatus = START_MEASURE;
		break;

	case (SEND_PERIOD * 8) + 8:
		timerStatus = GET_DATA;
		time_count = 0;
		break;
	}
}

uint32_t ws_GetCount()
{
	return wsCount;
}

void ws_data_i2c_init()
{
	i2c_ok = true;

	i2c_ok &= ina219_init();

	i2c_ok &= max44009_setAutomaticMode();

	i2c_ok &= ms5611_init();

	i2c_ok &= AS3935_Init();

	i2c_ok &= sht3x_init();
}

bool reInitI2C()
{
	if (HAL_I2C_DeInit(&hi2c1) != HAL_OK) {
		return false;
	}

	if (HAL_I2C_Init(&hi2c1) != HAL_OK)
	{
		return false;
	}
	/** Configure Analogue filter
	*/
	if (HAL_I2CEx_ConfigAnalogFilter(&hi2c1, I2C_ANALOGFILTER_ENABLE) != HAL_OK)
	{
		return false;
	}
	/** Configure Digital filter
	*/
	if (HAL_I2CEx_ConfigDigitalFilter(&hi2c1, 0) != HAL_OK)
	{
		return false;
	}

	// 传感器初始化
	ws_data_i2c_init();

	return i2c_ok;
}

void ws_data_init()
{
	// Init data
//	s_LastData = s_NewData = s_Data;
	Rain_Init();

	ws_data_i2c_init();

	// 启动1秒定时器
	HAL_LPTIM_TimeOut_Start_IT(&hlptim1, 0, 4095 - 1);
}

void ws_data_measure()
{
	// UV（一个周期无法完成，所以再提前一个周期，和MS5611的D2一起启动测量
//	VEML6075_Measure();

	// SHT35
	i2c_ok &= sht3x_measure();

	// max44009（功耗低，自动周期测量，无需独立触发）

	// ms5611（提前进行的D2，读取数据，并进行D1测量）
	i2c_ok &= ms5611_conversion_read_d2();
	i2c_ok &= ms5611_conversion_start_d1();

	// ads1115（太阳能辐射计）
	i2c_ok &= ADS1115_Start(0);

	// Wind
	Wind_Read();

	// INA219 测量
	i2c_ok &= ina219_measure();
}

void ws_data_get()
{
	float		bat_v, shunt_v;
	float		pressure = 0;
	float		lux = 0;
	int32_t		temperature, humidity;
	float		uva, uvb, uvi;

	// INA219
	shunt_v = getShuntVoltage_mV();
	bat_v = getBusVoltage_mV();
	// TODO:需要判断是否需要对取样电压进行修正
	bat_v += shunt_v;
	s_Data.battery_voltage = (uint16_t)bat_v;
	s_Data.battery_current = (int16_t)(getCurrent_mA() * 10);

	// MAX44009
	// TODO: 等待透明罩影响的校准结果
	i2c_ok &= max44009_getLux(&lux);
	s_Data.illumination = (uint32_t)lux;


	// MS5611
	i2c_ok &= ms5611_conversion_read_d1();
	i2c_ok &= ms5611_read_pressure(&pressure);
	s_Data.pressure = i2c_ok ? (uint16_t)pressure : 0;

	// 太阳能辐射(校准时已经考虑透明罩的影响）
	 i2c_ok &= ADS1115_Read(&s_Data.solar_radiation);
	 s_Data.solar_radiation *= 2;

	// 温度和湿度
	i2c_ok &= sht3x_read_temperature_and_humidity(&temperature, &humidity);
	s_Data.temperature = (int16_t)(temperature / 10);
	s_Data.humidity = (uint16_t)(humidity / 10);

	// UV
	// TODO：等待透明罩影响的校准
	i2c_ok &= VEML6075_GetUV(&uva, &uvb, &uvi);
	if (i2c_ok)
	{
		s_Data.uv_index = (uint16_t)(uvi * 100);
		s_Data.uva_value = (uint16_t)(uva * 100);
		s_Data.uvb_value = (uint16_t)(uvb * 100);
	}
	else
	{
		s_Data.uv_index = 0;
		s_Data.uva_value = 0;
		s_Data.uvb_value = 0;
	}

	// Wind
	Wind_GetData(&s_Data.wind_dir, &s_Data.wind_speed);

	// Rain
	s_Data.rain = GetRainTotal();

	// lightning
	enum INTERRUPT_STATUS	type;
	uint16_t			dist;
	i2c_ok &= AS3935_GetData(&type, &dist);
	if (i2c_ok) {
		s_Data.lightning_dist = dist;
	}
}

void ws_data_send()
{
	uint8_t		packSize;

	// 准备数据每次都发送
	s_Data.dataMask = DATA_BASIC;

	// firmware OTA状态
	if (s_Data.firmware != FIRMWARE_NONE)
	{
		s_Data.dataMask |= DATA_FIRMWARE;
	}

	// 发送
	packSize = packData(&s_Data, sendBuffer, sizeof(sendBuffer), &send_no);

	sendBufferSize = packSize;
	LoRA_TX(sendBuffer, sendBufferSize, ws_rxtimeout_callback, (void*)CMD_DATA);
}

void ws_data_resend()
{
	if (no_ack_count >= MAX_NO_ACK_COUNT)
	{
		// 多次未回应，表示主机可能关机，为节约电源，不再重发
		flag.sending = 0;
		return;
	}

	if (data_send_retry < MAX_RETRY)
	{
		// try again，将发送缓冲区的数据再发送一次
		data_send_retry++;
		LoRA_TX(sendBuffer, sendBufferSize, ws_rxtimeout_callback, (void*)CMD_DATA);
	}
	else
	{
		// No ack, 本次数据不再重发，No ack计数器累加
		no_ack_count++;

		// 复位单次重发计数器
		data_send_retry = 0;

		flag.sending = 0;
	}
}

void ws_firmwareget_resend()
{
	if (get_ota_retry < MAX_OTA_RETRY)
	{
		//
		get_ota_retry++;
		LoRA_TX(sendBuffer, sendBufferSize, ws_rxtimeout_callback, (void*)CMD_DATA);
	}
	else
	{
		// OTA数据获取超时，等待下一秒的数据发送后，再根据ACK标记重启流程
		get_ota_retry = 0;

		flag.sending = 0;
		flag.ota = 0;
		s_Data.firmware = FIRMWARE_NONE;
	}
}

void ws_rxtimeout_callback(void* data)
{
	uint8_t		rxtimeoutType = (uint8_t)(uint32_t)data;

	switch (rxtimeoutType)
	{
	case CMD_DATA:
		ws_data_resend();
		break;

	case CMD_FIRMWARE_GET:
		ws_firmwareget_resend();
		break;

	default:
		break;
	}
}

void ws_data_process_receive_buffer()
{
	if (receiveBufferSize == 0)
	{
		// 没有数据等待处理
		return;
	}

	// 解码缓冲区
	uint8_t		ret;
	ret = unpackData(receiveBuffer, receiveBufferSize, send_no);

	if (ret == 0) {
		return;
	}

	// 接收到回应，复位Watchdog
	HAL_IWDG_Refresh(&hiwdg);

	data_send_retry = 0;
	get_ota_retry = 0;
	no_ack_count = 0;

	flag.sending = 0;
}

static uint16_t	current_pos;

void ws_firmware_get_send()
{
	uint16_t	packSize;

	// 获取pos当前位置
	current_pos = GetOTAPos();

	// 发送
	packSize = packFirmwareGet(current_pos, sendBuffer, sizeof(sendBuffer), &send_no);

	sendBufferSize = packSize;
	LoRA_TX(sendBuffer, sendBufferSize, ws_rxtimeout_callback, (void*)CMD_FIRMWARE_GET);
}

void ws_data_ota()
{
	//
	if (!flag.ota) {
		return;
	}

	// 取数据，发送请求
	ws_firmware_get_send();

	flag.sending = 1;
}

// 返回是否可以回到低功耗状态
bool ws_data_process()
{
	bool returnLP = true;
	enum TimerStatus	temp;

	// 第一次，判断是否接收缓冲区有数据处理（确保在上次处理后，LoRA IRQ在中间过程中发生，而遗留数据在接收缓冲区）
	ws_data_process_receive_buffer();

	// 关闭全局中断，避免timerStatus判断时，刚好被修改
	__disable_irq();
	temp = timerStatus;
	timerStatus = TIMER_NONE;
	__enable_irq();

	switch (temp)
	{
	case START_MEASURE:
		ws_data_measure();
		break;

	case START_MEASURE_MS5611_1:
		// MS5611分两个阶段进行测量
		ms5611_conversion_start_d2();
		// VEML6075无法在125ms内完成一次测量，所以提前两个周期启动测量，这样就可以在最后一个周期获得测量数据。
		VEML6075_Measure();
		break;

	case GET_DATA:
		ws_data_get();
		if (i2c_ok) {
			flag.ws_data = 1;
		} else {
			// i2c传感器读取异常，需要重新初始化整个I2C总线和设备
			if (reInitI2C()) {
				i2c_reinit_count = 0;
			} else {
				i2c_reinit_count++;
				if (i2c_reinit_count > MAX_I2C_REINIT) {
					// 重启
					HAL_NVIC_SystemReset();
				}
			}
		}
		break;

	default:
		break;
	}

	if (flag.ws_data && !flag.sending) {
		// Send data and receive ack
		ws_data_send();
		flag.sending = 1;
		flag.ws_data = 0;

		// Refresh IWDG
//		HAL_IWDG_Refresh(&hiwdg);
	}

	// 第二次，判断是否接收缓冲区有数据处理
	ws_data_process_receive_buffer();

	// 判断是否需要进行升级数据获取
	if (!flag.sending) {
		ws_data_ota();
	}

	if (flag.ota) {
		returnLP = false;
	}

	return returnLP;
}

void ws_data_ack(WS_DATA_ACK* ack)
{
	bool		ret;
	bool		sameVer = false;

	switch(ack->data) {
	case FIRMWARE_INFO_NONE:
		break;

	case FIRMWARE_INFO_NEW:
		// 比对当前内存正在下载的版本
		ret = OTA_begin(&ack->firmware_info, &sameVer);

		if (ret) {
			// 开始或继续下载
			flag.ota = 1;
			if (sameVer) {
				s_Data.firmware = FIRMWARE_SAME;
			} else {
				s_Data.firmware = FIRMWARE_OTA;
			}
		} else {
			// 无需下载
		}
		break;

	case FIRMWARE_INFO_REBOOT:
		// reboot
		HAL_NVIC_SystemReset();
//		reInitI2C();
		break;

	case FIRMWARE_INFO_CONFIRM:
		flag.ota = 0;
		s_Data.firmware = FIRMWARE_NONE;
		OTA_reset();
		break;
	}
}

// TODO 增加是否成功的判断
void ws_firmware_data(uint8_t* buffer, uint8_t bufferSize)
{
	bool		ret;
	uint8_t		tempBuffer[LORA_OTA_PACKET_SIZE];
	bool		lastPack;

	if (bufferSize > LORA_OTA_PACKET_SIZE) {
		return;
	}

	memset(tempBuffer, 0, sizeof(tempBuffer));
	memcpy(tempBuffer, buffer, bufferSize);

	ret = OTA_data(current_pos, tempBuffer, bufferSize, &lastPack);
	if (!ret) {
		// 异常，不再继续
		flag.ota = 0;
		s_Data.firmware = FIRMWARE_ERROR;
	} else {
		if (lastPack) {
			// 正常这段代码是不会运行到的，因为成功后会自动重启
			flag.ota = 0;
			s_Data.firmware = FIRMWARE_SAME;
		}
	}
}




